為了確保我們的應用程式結構清晰且易於維護,我們先從首頁進行元件拆分。首先,我們將主頁上的 Card 和 Header 元件拆分出來,使其可以在其他頁面或模組中重用。
A) ProductCard
libs\iron-components\src\lib\ProductCard\ProductCard.tsx:
import { Box, Card, Text } from "@radix-ui/themes";
import React from "react";
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface ProductCardProps {}
const ProductCard: React.FC<ProductCardProps> = () => {
	return (
		<Card className="flex:1|1|20% flex:1|1|40%@<xs">
			<Box>
				<picture>
					<img src="https://www.w3schools.com/tags/img_girl.jpg" aria-hidden alt="Sample Image" width="100%" />
				</picture>
				<Text as="div" size="2" weight="bold">
					Teodros Girmay
				</Text>
				<Text as="div" size="2" color="gray">
					Engineering
				</Text>
			</Box>
		</Card>
	)
}
export default ProductCard;
libs\iron-components\src\lib\ProductCard\ProductCard.stories.tsx:
import { Meta, StoryObj } from "@storybook/react";
import ProductCard from "./ProductCard";
const meta: Meta<typeof ProductCard> = {
	component: ProductCard,
};
export default meta;
type Story = StoryObj<typeof ProductCard>;
export const Default: Story = {
	args: {},
};
libs\iron-components\src\lib\ProductCard\index.ts:
import ProductCard from "./ProductCard";
export default ProductCard;
B) Header
libs\iron-components\src\lib\Header\Header.tsx:
import React from "react";
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface HeaderProps {}
const Header: React.FC<HeaderProps> = () => {
	return (
		<header className="flex flex-direction:row jc:space-between ai:center p:1em bg:#333 color:#fff">
			<div className="flex-shrink:0 f:1.5rem f:bold">Iron Shop</div>
			<nav className="flex-grow:1 flex m:0|1em">
				<ul className="list-style:none flex gap:20px m:0 p:0 {cursor:pointer}>li">
					<li>Home</li>
					<li>Products</li>
				</ul>
			</nav>
		</header>
	)
}
export default Header;
libs\iron-components\src\lib\Header\Header.stories.tsx:
import { Meta, StoryObj } from "@storybook/react";
import Header from "./Header";
const meta: Meta<typeof Header> = {
	component: Header,
};
export default meta;
type Story = StoryObj<typeof Header>;
export const Default: Story = {
	args: {},
};
libs\iron-components\src\lib\Header\index.ts:
import Header from "./Header";
export default Header;
現在我們打開Storybook觀察我們的元件。
Header
ProductCard
接著,我們在libs\iron-components\src\index.ts添加導出我們元件的路徑:
export * from "./lib/Header";
export * from "./lib/ProductCard";
export * from "./lib/iron-components";
現在我們在我們的Next app中引入。現在我們稍微修改一下我們的layout和client元件
打開apps\iron-ecommerce-next\app\layout.tsx:
import { Flex } from "@radix-ui/themes";
import "@radix-ui/themes/styles.css";
import Header from "libs/iron-components/src/lib/Header";
import AppProvider from "./app-provider";
import "./global.css";
export const metadata = {
	title: "Welcome to iron-ecommerce-next",
	description: "Generated by create-nx-workspace"
};
const RootLayout = ({ children }: { children: React.ReactNode }) => {
	return (
		<html lang="en">
			<body>
				<AppProvider>
					<Flex direction="column">
						<Header />
						{children}
					</Flex>
				</AppProvider>
			</body>
		</html>
	);
};
export default RootLayout;
打開apps\iron-ecommerce-next\app\home.client.tsx:
"use client";
import { Flex } from "@radix-ui/themes";
import ProductCard from "libs/iron-components/src/lib/ProductCard";
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface Props {}
// eslint-disable-next-line no-empty-pattern
const HomeClient = ({}: Props) => {
	return (
		<Flex align="center" justify="center">
			<section className="flex flex:wrap flex-direction:row flex-basis:xs flex-basis:full@<xs gap:1rem jc:center">
				<div className="bg:blue w:100% h:10rem">Cover</div>
				{Array.from({ length: 32 }).map((_, i) => (
					<ProductCard key={i} />
				))}
			</section>
		</Flex>
	);
};
export default HomeClient;
現在我們已經將元件從頁面拆分出去了,接著,我們先將剩下的部分先完成,並且創建的部分有products、user和auth等頁面。並且下面是我們創建的結構。
a) products
///// apps\iron-ecommerce-next\app\products\page.tsx /////
import { NextPage } from "next";
import ProductsClient from "./products.client";
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface ProductsPageProps {}
const ProductsPage: NextPage<ProductsPageProps> = () => {
	return <ProductsClient />;
};
export default ProductsPage;
///// apps\iron-ecommerce-next\app\products\products.client.tsx /////
"use client";
import { Flex } from "@radix-ui/themes";
import ProductCard from "libs/iron-components/src/lib/ProductCard";
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface ProductsProps {}
// eslint-disable-next-line no-empty-pattern
const ProductsClient = ({}: ProductsProps) => {
	return (
		<Flex align="center" justify="center">
			<section className="flex flex:wrap flex-direction:row flex-basis:xs flex-basis:full@<xs gap:1rem jc:center">
				<div className="w:100% h:10rem">Products</div>
				{Array.from({ length: 32 }).map((_, i) => (
						<ProductCard key={i} />
					))}
			</section>
		</Flex>
	);
};
export default ProductsClient;

///// apps\iron-ecommerce-next\app\products\[id]\page.tsx /////
import { NextPage } from "next";
import ProductIdClient from "./productId.client";
interface ProductIdPageProps {
	params: {
		id: string;
	};
}
const ProductIdPage: NextPage<ProductIdPageProps> = ({ params }) => {
	const { id } = params;
	return <ProductIdClient productId={id} />;
};
export default ProductIdPage;
///// apps\iron-ecommerce-next\app\products\[id]\productId.client.tsx /////
"use client";
import { Flex } from "@radix-ui/themes";
import ProductCard from "libs/iron-components/src/lib/ProductCard";
interface ProductIdProps {
	productId?: string;
}
// eslint-disable-next-line no-empty-pattern
const ProductIdClient = ({productId}: ProductIdProps) => {
	return (
			<Flex align="center" justify="center">
				<section className="flex flex:wrap flex-direction:row flex-basis:xs flex-basis:full@<xs gap:1rem jc:center">
					<div className="w:100% h:10rem">Product: {productId}</div>
					<ProductCard />
				</section>
			</Flex>
	);
};
export default ProductIdClient;

b) user
///// apps\iron-ecommerce-next\app\user\[id]\page.tsx /////
import { NextPage } from "next";
import UserIdClient from "./userId.client";
interface UserIdPageProps {
	params: {
		id: string;
	};
}
const UserIdPage: NextPage<UserIdPageProps> = ({ params }) => {
	const { id } = params;
	return <UserIdClient userId={id} />;
};
export default UserIdPage;
///// apps\iron-ecommerce-next\app\user\[id]\userId.client.tsx /////
"use client";
import { Flex } from "@radix-ui/themes";
interface UserIdProps {
	userId?: string;
}
// eslint-disable-next-line no-empty-pattern
const UserIdClient = ({userId}: UserIdProps) => {
	return (
		<Flex align="center" justify="center">
			<section className="flex flex:wrap flex-direction:row flex-basis:xs flex-basis:full@<xs gap:1rem jc:center">
				<div className="w:100% h:10rem bg:grey">User {userId} Block</div>
			</section>
		</Flex>
	);
};
export default UserIdClient;

///// apps\iron-ecommerce-next\app\user\auth\page.tsx /////
import { NextPage } from "next";
import UserAuthClient from "./userAuth.client";
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface UserIdPageProps {}
const UserAuthPage: NextPage<UserIdPageProps> = () => {
	return <UserAuthClient />;
};
export default UserAuthPage;
///// apps\iron-ecommerce-next\app\user\auth\userAuth.client.tsx /////
"use client";
import { Flex } from "@radix-ui/themes";
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface UserAuthProps {}
// eslint-disable-next-line no-empty-pattern
const UserAuthClient = ({}: UserAuthProps) => {
	return (
		<Flex align="center" justify="center">
			<section className="flex flex:wrap flex-direction:row flex-basis:xs flex-basis:full@<xs gap:1rem jc:center">
				<div className="w:100% h:10rem bg:grey">User Auth Block</div>
			</section>
		</Flex>
	);
};
export default UserAuthClient;

現在我們將部分的頁面先創建出來,之後我們在實現內部的細節。
並且當我們進行更多的開發,我們可能會需要一直創建組件。手動創建這些組件可能會很耗時,並且容易出錯。為了提高效率,我們將使用 NX 提供的工具來自動化這個過程。
今天,我們在Next.js應用程式中拆分和重用組件,並通過使用 Storybook,我們可以更加直觀地理解和展示我們的組件。接著我們將一些頁面先創建起來。最後,為了進一步提高我們的開發效率,明天我們將介紹如何使用NX來達成自動化創建組件。
樓主你好~
我在此章節執行
git add .
pnpm run commit
後
一直報以下錯誤
✖ nx affected:lint:
 >  NX   Affected criteria defaulted to --base=main --head=HEAD
 >  NX   Running target lint for 3 projects:
    - iron-ecommerce-next
    - iron-ecommerce-next-e2e
    - iron-components
   
   With additional flags:
     apps/iron-ecommerce-next/app/home.client.tsx apps/iron-ecommerce-next/app/layout.tsx apps/iron-ecommerce-next/app/products/[id]/page.tsx apps/iron-ecommerce-next/app/products/[id]/productId.client.tsx apps/iron-ecommerce-next/app/products/page.tsx apps/iron-ecommerce-next/app/products/products.client.tsx apps/iron-ecommerce-next/app/user/[id]/page.tsx apps/iron-ecommerce-next/app/user/[id]/userId.client.tsx apps/iron-ecommerce-next/app/user/auth/page.tsx apps/iron-ecommerce-next/app/user/auth/userAuth.client.tsx libs/iron-components/src/index.ts libs/iron-components/src/lib/Header/Header.stories.tsx libs/iron-components/src/lib/Header/Header.tsx libs/iron-components/src/lib/Header/index.ts libs/iron-components/src/lib/ProductCard/ProductCard.stories.tsx libs/iron-components/src/lib/ProductCard/ProductCard.tsx libs/iron-components/src/lib/ProductCard/index.ts
 
> nx run iron-components:lint apps/iron-ecommerce-next/app/home.client.tsx apps/iron-ecommerce-next/app/layout.tsx apps/iron-ecommerce-next/app/products/[id]/page.tsx apps/iron-ecommerce-next/app/products/[id]/productId.client.tsx apps/iron-ecommerce-next/app/products/page.tsx apps/iron-ecommerce-next/app/products/products.client.tsx apps/iron-ecommerce-next/app/user/[id]/page.tsx apps/iron-ecommerce-next/app/user/[id]/userId.client.tsx apps/iron-ecommerce-next/app/user/auth/page.tsx apps/iron-ecommerce-next/app/user/auth/userAuth.client.tsx libs/iron-components/src/index.ts libs/iron-components/src/lib/Header/Header.stories.tsx libs/iron-components/src/lib/Header/Header.tsx libs/iron-components/src/lib/Header/index.ts libs/iron-components/src/lib/ProductCard/ProductCard.stories.tsx libs/iron-components/src/lib/ProductCard/ProductCard.tsx libs/iron-components/src/lib/ProductCard/index.ts
Linting "iron-components"...
Pages directory cannot be found at apps/iron-ecommerce-next/pages. If using a custom path, please configure with the `no-html-link-for-pages` rule in your eslint config file.
/Users/chihhao/iron-ecommerce-org/apps/iron-ecommerce-next/app/home.client.tsx
  3:1  error  Projects cannot be imported by a relative or absolute path, and must begin with a npm scope  @nx/enforce-module-boundaries
/Users/chihhao/iron-ecommerce-org/apps/iron-ecommerce-next/app/layout.tsx
  3:1  error  Projects cannot be imported by a relative or absolute path, and must begin with a npm scope  @nx/enforce-module-boundaries
/Users/chihhao/iron-ecommerce-org/apps/iron-ecommerce-next/app/products/products.client.tsx
  3:1  error  External resources cannot be imported using a relative or absolute path  @nx/enforce-module-boundaries
✖ 3 problems (3 errors, 0 warnings)
✖ 3 problems (3 errors, 0 warnings)
> nx run iron-ecommerce-next-e2e:lint apps/iron-ecommerce-next/app/home.client.tsx apps/iron-ecommerce-next/app/layout.tsx apps/iron-ecommerce-next/app/products/[id]/page.tsx apps/iron-ecommerce-next/app/products/[id]/productId.client.tsx apps/iron-ecommerce-next/app/products/page.tsx apps/iron-ecommerce-next/app/products/products.client.tsx apps/iron-ecommerce-next/app/user/[id]/page.tsx apps/iron-ecommerce-next/app/user/[id]/userId.client.tsx apps/iron-ecommerce-next/app/user/auth/page.tsx apps/iron-ecommerce-next/app/user/auth/userAuth.client.tsx libs/iron-components/src/index.ts libs/iron-components/src/lib/Header/Header.stories.tsx libs/iron-components/src/lib/Header/Header.tsx libs/iron-components/src/lib/Header/index.ts libs/iron-components/src/lib/ProductCard/ProductCard.stories.tsx libs/iron-components/src/lib/ProductCard/ProductCard.tsx libs/iron-components/src/lib/ProductCard/index.ts
Linting "iron-ecommerce-next-e2e"...
Pages directory cannot be found at apps/iron-ecommerce-next/pages. If using a custom path, please configure with the `no-html-link-for-pages` rule in your eslint config file.
/Users/chihhao/iron-ecommerce-org/apps/iron-ecommerce-next/app/home.client.tsx
  3:1  error  Projects cannot be imported by a relative or absolute path, and must begin with a npm scope  @nx/enforce-module-boundaries
/Users/chihhao/iron-ecommerce-org/apps/iron-ecommerce-next/app/layout.tsx
  3:1  error  Projects cannot be imported by a relative or absolute path, and must begin with a npm scope  @nx/enforce-module-boundaries
/Users/chihhao/iron-ecommerce-org/apps/iron-ecommerce-next/app/products/products.client.tsx
  3:1  error  External resources cannot be imported using a relative or absolute path  @nx/enforce-module-boundaries
✖ 3 problems (3 errors, 0 warnings)
✖ 3 problems (3 errors, 0 warnings)
> nx run iron-ecommerce-next:lint apps/iron-ecommerce-next/app/home.client.tsx apps/iron-ecommerce-next/app/layout.tsx apps/iron-ecommerce-next/app/products/[id]/page.tsx apps/iron-ecommerce-next/app/products/[id]/productId.client.tsx apps/iron-ecommerce-next/app/products/page.tsx apps/iron-ecommerce-next/app/products/products.client.tsx apps/iron-ecommerce-next/app/user/[id]/page.tsx apps/iron-ecommerce-next/app/user/[id]/userId.client.tsx apps/iron-ecommerce-next/app/user/auth/page.tsx apps/iron-ecommerce-next/app/user/auth/userAuth.client.tsx libs/iron-components/src/index.ts libs/iron-components/src/lib/Header/Header.stories.tsx libs/iron-components/src/lib/Header/Header.tsx libs/iron-components/src/lib/Header/index.ts libs/iron-components/src/lib/ProductCard/ProductCard.stories.tsx libs/iron-components/src/lib/ProductCard/ProductCard.tsx libs/iron-components/src/lib/ProductCard/index.ts
Linting "iron-ecommerce-next"...
Pages directory cannot be found at apps/iron-ecommerce-next/pages. If using a custom path, please configure with the `no-html-link-for-pages` rule in your eslint config file.
/Users/chihhao/iron-ecommerce-org/apps/iron-ecommerce-next/app/home.client.tsx
  3:1  error  Projects cannot be imported by a relative or absolute path, and must begin with a npm scope  @nx/enforce-module-boundaries
/Users/chihhao/iron-ecommerce-org/apps/iron-ecommerce-next/app/layout.tsx
  3:1  error  Projects cannot be imported by a relative or absolute path, and must begin with a npm scope  @nx/enforce-module-boundaries
/Users/chihhao/iron-ecommerce-org/apps/iron-ecommerce-next/app/products/products.client.tsx
  3:1  error  External resources cannot be imported using a relative or absolute path  @nx/enforce-module-boundaries
✖ 3 problems (3 errors, 0 warnings)
✖ 3 problems (3 errors, 0 warnings)